import gurobipy as gp
from gurobipy import *
from gurobipy import GRB

try:

    # ①创建一个模型,gurobipy.Model('模型名')
    m = gp.Model("TTE_test")

    # ②设置参数变量   x = MODEL.addVars(20, 8, vtype=gurobipy.GRB.BINARY,'C')  Cij  i∈[0,20) j∈[0,8)
    C = m.addVars(range(0, 10), range(1, 10), vtype='C', name="C")

    M = m.addVar(lb=0, ub=gurobipy.GRB.INFINITY, vtype=GRB.SEMIINT, name='M')
    '''相当于一下设置了下面Z1-Z7，用Z[],表示下标'''
    Z = m.addVars(20, vtype=GRB.BINARY, name='Z')

    # 设置全局优化方向，所有目标的方向都是一致的，默认值为1表示求最小值，-1表示最大化，可以通过乘-1来调优化方向方向
    m.ModelSense = GRB.MINIMIZE

    Q = [[1, 1, 7, 8, 9], [2, 2, 7, 9], [3, 2, 7, 10], [4, 3, 7, 10], [5, 4, 7, 9], [6, 4, 7, 10], [7, 5, 8, 7, 9], [8, 6, 7, 9], [9, 6, 7, 10]]
    # ③设置目标函数 首先定义一个线性表达式
    obj1 = LinExpr(0)
    for i in range(len(Q)):
                #将目标函数系数与决策变量相乘，并进行连加
                    obj1.addTerms(1, C[ Q[i][0], Q[i][len(Q[i])-2] ] )
    #将表示目标函数的线性表达式加入模型
    m.setObjectiveN(obj1, index=0,priority=2, abstol=0, reltol=0, name='obj1')

    obj2 = LinExpr(0)  # 让初始发送时间尽可能小
    for i in range(len(Q)):
        obj2.addTerms(1, C[Q[i][0], Q[i][1]])
    m.setObjectiveN(obj2, index=1, priority=1, abstol=0, reltol=0, name='obj2')

    # m.setObjectiveN(C[1,8]+C[2,7]+C[3,7]+C[4,7]+C[5,7]+C[6,7]+C[7,7]+C[8,7]+C[9,7], index=0,priority=2, abstol=0, reltol=0, name='obj1')
    # m.setObjectiveN(C[1, 1] + C[2, 2] + C[3, 2] + C[4, 3] + C[5, 4] + C[6, 4] + C[7, 5] + C[8, 6] + C[9, 6], index=1,priority=1, abstol=0, reltol=0, name='obj2')

    '''L1_7:表示在结点1到3这条链路上 简单分析，需要创建一个多维数组用来形成指定链路上的约束条件，'''
    # ④设置约束条件 #第一个元素的[7,9]是指这条路的端结点，后面开始依次代表[业务号,帧长]
    L1_7 =  [ [1, 7], [1, 1]                                 ]
    L2_7 =  [ [2, 7], [2, 1], [3, 2]                         ]
    L3_7 =  [ [3, 7], [4, 2]                                 ]
    L4_7 =  [ [4, 7], [5, 3], [6, 3]                         ]
    L5_7 =  [ [5, 7]                                         ]
    L5_8 =  [ [5, 8], [7, 4]                                 ]
    L6_7 =  [ [6, 7], [8, 4], [9, 4]                         ]
    L7_8 =  [ [7, 8], [1, 1]                                 ]
    L7_9 =  [ [7, 9], [1, 1], [2, 1], [5, 3], [7, 4], [8, 4] ]
    L7_10 = [ [7, 10],[3, 2], [4, 2], [6, 3], [9, 4]         ]
    L8_7 =  [ [8, 7], [7, 4]                                 ]
    L8_9 =  [ [8, 9], [1, 1]                                 ]

    k=0
    def Add_Constrs(Ln_m):  # 参数是一个二维数组
        global k
        for i in range(1, len(Ln_m) - 1):
            for j in range(i + 1, len(Ln_m)):
                k += 1
                m.addConstr(C[Ln_m[i][0], Ln_m[0][0]] + Ln_m[i][1] <= C[Ln_m[j][0], Ln_m[0][0]] + Z[k] * M, '')
                m.addConstr(C[Ln_m[j][0], Ln_m[0][0]] + Ln_m[j][1] <= C[Ln_m[i][0], Ln_m[0][0]] + (1 - Z[k]) * M, '')

    Add_Constrs(L1_7)
    Add_Constrs(L2_7)
    Add_Constrs(L3_7)
    Add_Constrs(L4_7)
    Add_Constrs(L5_7)
    Add_Constrs(L5_8)
    Add_Constrs(L6_7)
    Add_Constrs(L7_8)
    Add_Constrs(L7_9)
    Add_Constrs(L7_10)
    Add_Constrs(L8_7)
    Add_Constrs(L8_9)

    '''Q数组，每一行代表一个业务号的完整路径，每一行中的元素代表经过的端结点和交换机 譬如 1 7 8 9 表示业务0 经过 
    端结点1——结点7(交换机3)——结点8(交换机8)——端结点9'''
    Q = [   [1, 1, 7, 8, 9],    #业务1 走 1-7-8-9
            [2, 2, 7, 9],       #业务2 走 2-7-9
            [3, 2, 7, 10],      #业务3 走 2-7-10
            [4, 3, 7, 10],      #业务4 走 3-7-10
            [5, 4, 7, 9],       #业务5 走 4-7-9
            [6, 4, 7, 10],      #业务6 走 4-7-10
            [7, 5, 8, 7, 9],    #业务7 走 5-8-7-9
            [8, 6, 7, 9],       #业务8 走 6-7-9
            [9, 6, 7, 10]   ]   #业务9 走 6-7-10

    '''  F列表存放的是对应业务经过不同路径的时延损耗和（所有乱七八糟时间和）'''
    F = [   [2.5, 3, 2],     # 业务1 路径1-7上的时延：2.5 , 在路径7-8上的时延：3 , 路径8-9上的时延：2....一直到业务到达
            [2.5, 2],        # 业务2 路径2-7上的时延：2.5 , 在路径7-8上的时延：2 ,....一直到业务到达
            [2.5, 2],        # 业务3 路径2-7上的时延：2.5 , 在路径7-9上的时延：2 ,....一直到业务到达
            [2.5, 2],        # 业务4 路径3-7上的时延：2.5 , 在路径7-9上的时延：2 ,....一直到业务到达
            [2.5, 2],        # ......
            [2.5, 2],        # 现假设其他所有业务在各个路上的时延都一样，且都是经过一跳到达目的地
            [2.5, 1.5, 2],   #  注意 ：
            [2.5, 2],        #    对于同一个业务一共经过n个端点，实际上整个路径上的约束条件一共只有n-2个，业务走过的每一段路都需要与其相邻的
            [2.5, 2],   ]    #    前一段路做短结点发送时刻的比较，直到倒数第二段路径的发送时刻+该段的时延一起与最后一段路径的发送时刻也约束完，
    '''对于这个F，因为我们除了业务1和7以外只有一跳，通过的端结点是3个，我们只用的到每个业务第一段路径的时延 2.5 '''
    for i in range(len(Q)):
        for j in range(0, len(Q[i]) - 3):
            m.addConstr(C[i+1, Q[i][j+1]] + F[i][j] <= C[i+1, Q[i][j + 2]])  # （i+1） ：表示业务序号（行号索引从0开始）

    m.write('Test_9point.lp')

    env0 = m.getMultiobjEnv(0)
    env1 = m.getMultiobjEnv(1)
    env0.setParam('TimeLimit', 100)
    env1.setParam('TimeLimit', 10)
    #m.setParam(GRB.Param.LogFile,"rizhi.log")
    #m.setParam(GRB.Param.MIPGap,0 )
    m.Params.MIPGap = 0
    m.setParam(GRB.Param.TimeLimit,14400.0 )
    #m.Params.NoRelHeurtime=10
    m.Params.MIPFous = 1
    # ⑤开始求解
    m.optimize()
    m.discardMultiobjEnvs()


    # '''循环遍历你设置的所有变量值'''
    # for v in m.getVars():
    #     print('%s %g' % (v.varName, v.x))

    '''一行一下输出所有设置的变量值'''
    v = m.getVars()
    print(v)
    print('\n---------------------------------------------')
    '''循环输出我想要的变量'''
    # for i in range(1,9):
    #     print(C[i,7])

    # print('\n-----------------------------------------------')
    # wangzai=C[2,7]
    # print('wangzai=',C[2,7])

    '''显示设置的一个目标，如果设置了多个目标，返回优先级最大的'''
    print('Obj1: %g' % m.objVal)  # objVal：（优化后最优的目标结果值）

    # print(m.NumObj) 输出结果为2 ，因为我们设置了两个目标函数, 序号 index=0 ,index=1
    for i in range(m.NumObj):  # 可以使用NumObj属性查询(或修改)模型中的目标数量，它的值是目标函数的数量。遍历它，得到的是一个个index的值
        m.setParam(GRB.Param.ObjNumber, i)
        print('Obj%d = ' % (i + 1), m.ObjNVal)


except gp.GurobiError as e:
    print('Error code ' + str(e.errno) + ': ' + str(e))

except AttributeError:  # 属性错误
    print('Encountered an attribute error')



